package bag

import (
	"errors"
	"log"
)



func createBlist(id uint16) (*BagList,error){
	var err error
	b:=&BagList{}
	b.root,err=BMan.GetItem(id)
	if err!=nil{
		log.Println("segment fault from bag pool")
		return nil,errors.New("no such item")
	}
	b.length=0
	return b,nil	
}

func (b *BagMan)GetItem(id uint16) (*Item,error){ 
	l:=id/100
	if int(l)>=len(BMan.data){
		log.Println("segment fault from bag pool")
		return nil,errors.New("no such item")
	}
	return &BMan.data[l][id%100],nil
}

func (b *BagMan)SetItemLast(id uint16,last uint16) error{
	l:=id/100
	if int(l)>=len(BMan.data){
		log.Println("segment fault from bag pool")
		return errors.New("no such item")
	}
	BMan.data[l][id%100].Last=last
	return nil
}

func (b *BagMan)SetItemNext(id uint16,next uint16) error{
	l:=id/100
	if int(l)>=len(BMan.data){
		log.Println("segment fault from bag pool")
		return errors.New("no such item")
	}
	BMan.data[l][id%100].Next=next
	return nil
}

func(b *BagMan)GetItemNext(id uint16) uint16{
	l:=id/100
	if int(l)>=len(BMan.data){
		log.Println("segment fault from bag pool")
		return 0
	}
	return BMan.data[l][id%100].Next
}

func (b *BagMan)GetItemLast(id uint16) uint16{
	l:=id/100
	if int(l)>=len(BMan.data){
		log.Println("segment fault from bag pool")
		return 0
	}
	return BMan.data[l][id%100].Last
}

func (li *BagList)PushBack(id uint16) error { 
	li.length++
	last,err:=BMan.GetItem(li.root.Last)
	if err!=nil{
		return err
	}
	node,err:=BMan.GetItem(id)
	if err!=nil{
		return err
	}

	node.Next=last.Next
	last.Next=id
	node.Last=li.root.Last
	li.root.Last=id

	return nil
}

func (li *BagList)PushList(other *BagList) (err error) {
	begin:=other.root.Next
	end:=other.root.Last
	lbegin:=li.root.Last
	lend:=BMan.GetItemNext(lbegin)
	err=BMan.SetItemLast(begin,lbegin)
	if err!=nil{
		return err
	}
	err=BMan.SetItemNext(end,lend)
	if err!=nil{
		return err
	}

	li.root.Last=end
	err=BMan.SetItemNext(lbegin,begin)
	return err
}

func (li *BagList) Remove(id uint16) error {
	current := li.root.Next
	for current != 0 {
		if current == id {
			prev := BMan.GetItemLast(current)
			next := BMan.GetItemNext(current)

			if err := BMan.SetItemNext(prev, next); err != nil {
				return err
			}

			if next != 0 {
				if err := BMan.SetItemLast(next, prev); err != nil {
					return err
				}
			} else {
				li.root.Last = prev
			}

			li.length--
			return nil
		}
		current = BMan.GetItemNext(current)
	}
	return errors.New("item not found")
}

func (li *BagList)DeleteList() error{	
	root:=BMan.GetItemLast(li.root.Next)
	BMan.mu.Lock()
	defer BMan.mu.Unlock()
	err:=BMan.Unused.PushList(li)
	if err!=nil{
		return err
	}
	err=BMan.Unused.PushBack(root)
	return err
}

func (li *BagList)Length() int {
	return li.length
}

func (li *BagList) Pop() (back uint16,err error) { 
	if li.length==0{
		return 0,errors.New("list is empty")
	}
	li.length--
	back=li.root.Next
	next:=BMan.GetItemNext(back)
	if next == 0{
		return back,errors.New("segment fault Pop out of range")
	} 

	li.root.Next=next
	err=BMan.SetItemLast(next,BMan.GetItemLast(back))

	return back,err
}

func Init() (err error) {
	BMan=new(BagMan)

	BMan.mu.Lock()
	defer BMan.mu.Unlock()

	buffer := make([]Item,100)
	BMan.data = make([][]Item,0,10)
	BMan.data=append(BMan.data, buffer)
	BMan.Unused,err=createBlist(0)
	if err!=nil{
		return err
	}
	for i:=1;i<100;i++{
		BMan.Unused.PushBack(uint16(i))
	}
	return nil
}


func (Bp *BagMan)up() {
	Bp.mu.Lock()
	defer Bp.mu.Unlock()
	buffer:=make([]Item,100)
	Bp.data=append(Bp.data, buffer)
	begin:=uint16(len(Bp.data)*100)
	end:=uint16(len(Bp.data)*100+100)
	for i:=begin;i<end;i++{
		Bp.Unused.PushBack(i)
	}
}

func (Bp *BagMan)PopId() (id uint16,err error){
	Bp.mu.Lock()
	defer Bp.mu.Unlock()
	if Bp.Unused.Length()==0{
		Bp.up()
	}
	return Bp.Unused.Pop()
}

func (Bp *BagMan)Push (id uint16) error{
	Bp.mu.Lock()
	defer Bp.mu.Unlock()
	return Bp.Unused.PushBack(id)
}